package com.imhuis.pay.wxpay;

import com.imhuis.pay.wxpay.exception.WechatPayRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

import static com.imhuis.pay.wxpay.WxPayConstants.*;

/**
 * https://www.magese.com/2021/01/20/Java-%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98%EF%BC%88jsapi%EF%BC%89V2%E5%8F%8AV3%E7%89%88%E6%9C%AC/#%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98V3%E7%89%88%E6%9C%AC
 *
 * https://github.com/vnjohn/springboot-pay
 */
public class WxPayUtil {

    static Logger log = LoggerFactory.getLogger(WxPayUtil.class);

    /**
     * 支付签名
     *
     * @param msg        要签名的数据
     * @param privateKey 证书秘钥
     * @return 签名
     */
    public static String paySign(String msg, PrivateKey privateKey) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(privateKey);
            sign.update(msg.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (NoSuchAlgorithmException e) {
            log.error("paySign", "微信支付 - 支付签名失败", "当前Java环境不支持SHA256withRSA");
            throw new WechatPayRuntimeException("当前Java环境不支持SHA256withRSA", e);
        } catch (SignatureException e) {
            log.error("paySign", "微信支付 - 支付签名失败", "签名计算失败");
            throw new WechatPayRuntimeException("签名计算失败", e);
        } catch (InvalidKeyException e) {
            log.error("paySign", "微信支付 - 支付签名失败", "无效的私钥");
            throw new WechatPayRuntimeException("无效的私钥", e);
        }
    }

    /**
     * 解密回调数据V3版
     *
     * @param associatedData 附加数据
     * @param nonce          随机字符串
     * @param ciphertext     数据密文
     * @param apiV3Key       V3秘钥
     * @return 解密后的JSON数据
     */
    public static String decryptNotifyV3(String associatedData, String nonce, String ciphertext, String apiV3Key) {
        byte[] nonceBytes = nonce.getBytes(StandardCharsets.UTF_8);
        byte[] apiV3KeyBytes = apiV3Key.getBytes(StandardCharsets.UTF_8);
        if (apiV3KeyBytes.length != APIV3_KEY_LENGTH_BYTE) {
            throw new IllegalArgumentException("无效的ApiV3Key，长度必须为32个字节");
        }
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING_V3);
            SecretKeySpec key = new SecretKeySpec(apiV3KeyBytes, ALGORITHM);
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonceBytes);
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData.getBytes());
            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            log.error("decryptNotifyV3", "微信支付 - 解密V3回调数据失败", e.getMessage());
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException | BadPaddingException | IllegalBlockSizeException e) {
            log.error("decryptNotifyV3", "微信支付 - 解密V3回调数据失败", e.getMessage());
            throw new IllegalArgumentException(e);
        }
    }

}
